/***************************************************************************
 *   Copyleft 2008-2009 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./vGlState.h"
#include "../../Macros/vMacros.h"			

namespace Vendetta {
	vGlState::vGlState() {
		// TEST
		mNumTextureUnits = 1;
		/*	glGetIntegerv(GL_MAX_ACTIVE_TEXTURES_ARB, &NumTextureUnits); GL_ASSERT
		if (NumTextureUnits < 2) {
		LOG << "Minimum 2 texture units are required to run Monster Engine\n";
		throw cException("We're sorry but minimum of 2 texture units are required to run Monster Engine !");
		}
		*/	
		mTexturesBinds = vArray<vUInt>(mNumTextureUnits);
		mEnabledUnits = vArray<vBool>(mNumTextureUnits);
		mCurrentUnit = 0;

		initTerms();
		Reset();
	}

	vGlState::~vGlState() {
	}

	// --------------------------------------------------
	//	SetTexture
	// --------------------------------------------------
/*
	vVoid vGlState::SetTexture(const cTextureHandler &textureHandler, const vInt textureUnit) {
		if (!textureHandler.Texture) {
 return;
		}

		// FIXME: State keeps no information on RenderTarget being bound
		// FIXME: Also no RenderTarget->Unbind() method is used, so nothing works yet
		/*if (texture.RenderTarget) {
 if (mCurrentUnit != textureUnit) {
 	mCurrentUnit = textureUnit;
 	glActiveTextureARB(GL_TEXTURE0_ARB + mCurrentUnit); GL_ASSERT
 }
 texture.RenderTarget->bind();
 cEngine::Stats->incTextureBinds();	FIXME
 return;
		}*/
		/*if (textureHandler.Texture->BindID != mTexturesBinds[textureUnit]) {
 if (mCurrentUnit != textureUnit) {
 	mCurrentUnit = textureUnit;
	// GL::glActiveTextureARB(GL_TEXTURE0_ARB + mCurrentUnit); GL_ASSERT
 }
 mTexturesBinds[mCurrentUnit] = textureHandler.Texture->BindID;
 glBindTexture(textureHandler.Texture->Target, textureHandler.Texture->BindID); GL_ASSERT
	//		cEngine::Stats->incTextureBinds();
		}
	}*/

	// --------------------------------------------------
	//	SetTexture 2
	// --------------------------------------------------
	/*
	vVoid vGlState::SetTexture(const cTextureHandler &textureHandler) {
		SetTexture(textureHandler, 0);
	}*/

	// --------------------------------------------------
	//	SetBlankTexture
	// --------------------------------------------------
/*
	vVoid vGlState::SetBlankTexture(const vInt textureUnit) {
		if (mTexturesBinds[textureUnit] != 0) {
 if (mCurrentUnit != textureUnit) {
 	mCurrentUnit = textureUnit;
 	GL::glActiveTextureARB(GL_TEXTURE0_ARB + mCurrentUnit); GL_ASSERT
 }
 mTexturesBinds[mCurrentUnit] = 0;
 glBindTexture(GL_TEXTURE_2D, 0); GL_ASSERT
 //cEngine::Stats->incTextureBinds();	FIXME
		}
	}*/

	// --------------------------------------------------
	//	EnableTexture
	// --------------------------------------------------

	vVoid vGlState::EnableTexture(const vInt textureUnit) {
		//if (mEnabledUnits[textureUnit]) {
		//	return;
		//}
		//if (mCurrentUnit != textureUnit) {
		//	mCurrentUnit = textureUnit;
		////		GL::glActiveTextureARB(GL_TEXTURE0_ARB + mCurrentUnit); GL_ASSERT
		//}
		//mEnabledUnits[mCurrentUnit] = true;
		//glEnable(GL_TEXTURE_2D); GL_ASSERT
	}

	// --------------------------------------------------
	//	EnableTexture 2
	// --------------------------------------------------

	vVoid vGlState::EnableTexture()	{
		EnableTexture(0);
	}

	// --------------------------------------------------
	//	DisableTexture
	// --------------------------------------------------

	vVoid vGlState::DisableTexture(const vInt textureUnit) {
	//	if (!mEnabledUnits[textureUnit]) {
	//		return;
	//	}
	//	if (mCurrentUnit != textureUnit) {
	//		mCurrentUnit = textureUnit;
	////		GL::glActiveTextureARB(GL_TEXTURE0_ARB + mCurrentUnit); GL_ASSERT
	//	}
	//	glDisable(GL_TEXTURE_2D); GL_ASSERT
	//	mEnabledUnits[mCurrentUnit] = false;
	}

	// --------------------------------------------------
	//	DisableTexture
	// --------------------------------------------------

	vVoid vGlState::DisableTexture() {
		DisableTexture(0);
	}

	// --------------------------------------------------
	//	reSetTexture
	// --------------------------------------------------

	vVoid vGlState::ResetTexture(const vInt textureUnit) {
		mTexturesBinds[textureUnit] = 0;
	}


	vVoid vGlState::EnableTransparency() {
		SetBlend(ON);
		SetBlendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA);
	}

	vVoid vGlState::DisableTransparency() {
		SetBlend(OFF);
	}

	// --------------------------------------------------
	//	SetBlend
	// --------------------------------------------------

	vVoid vGlState::SetBlend(const vInt blend) {
		if (mBlend == blend) {
			return;
		}
		mBlend = blend;
		if (mBlend != OFF) {
			glEnable(GL_BLEND); V_GL_ASSERT
		} else {
			glDisable(GL_BLEND); V_GL_ASSERT
		}
	}

	// --------------------------------------------------
	//	SetBlendFunc
	// --------------------------------------------------

	vVoid vGlState::SetBlendFunc(const vInt blendSrc, const vInt blendDst) {
		if ((mBlendSrc == blendSrc) &&	(mBlendDst == blendDst)) {
			return;
		}
		mBlendSrc = blendSrc;
		mBlendDst = blendDst;
		glBlendFunc(mBlendSrc, mBlendDst); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetmAlphaTest
	// --------------------------------------------------

	vVoid vGlState::SetAlphaTest(const vInt alphaTest) {
		if (mAlphaTest == alphaTest) {
			return;
		}
		mAlphaTest = alphaTest;
		if (mAlphaTest != OFF) {
			glEnable(GL_ALPHA_TEST); V_GL_ASSERT
		} else {
			glDisable(GL_ALPHA_TEST); V_GL_ASSERT
		}
	}

	// --------------------------------------------------
	//	SetmAlphaFunc
	// --------------------------------------------------

	vVoid vGlState::SetAlphaFunc(const vInt alphaFunc, const vReal alphaValue) {
		if ((mAlphaFunc == alphaFunc) && (mAlphaValue == alphaValue)) {
			return;
		}
		mAlphaFunc = alphaFunc;
		mAlphaValue = alphaValue;
		glAlphaFunc(mAlphaFunc, mAlphaValue); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetColor
	// --------------------------------------------------

	vVoid vGlState::SetColor(const vReal red, const vReal green, const vReal blue, const vReal alpha) {
		mColor.R = red;
		mColor.G = green;
		mColor.B = blue;
		mColor.A = alpha;
		glColor4fv((float*) &mColor); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetColor 2
	// --------------------------------------------------

	vVoid vGlState::SetColor(const vColor &color, const vReal alphaMultiplier) {
		mColor.R = color.R;
		mColor.G = color.G;
		mColor.B = color.B;
		mColor.A = color.A * alphaMultiplier;
		glColor4fv((float*) &mColor); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetColor 3
	// --------------------------------------------------

	vVoid vGlState::SetColor(const vColor &color) {
		if (mColor == color) {
			return;		
		}
		mColor = color;
		glColor4fv((float*) &mColor); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetColorMask
	// --------------------------------------------------

	vVoid vGlState::SetColorMask(const vInt redMask, const vInt greenMask, const vInt blueMask, const vInt alphaMask) {
		if ((mRedMask == redMask) && (mGreenMask == greenMask) &&
			(mBlueMask == blueMask) && (mAlphaMask == alphaMask)) {
			return;
		}
		mRedMask = redMask;
		mGreenMask = greenMask;
		mBlueMask = blueMask;
		mAlphaMask = alphaMask;
		glColorMask(mRedMask, mGreenMask, mBlueMask, mAlphaMask); V_GL_ASSERT
	}

	vVoid vGlState::SetClearColor(const vColor &color) {
		if (mClearColor == color) {
			return;
		}
		mClearColor = color;
		glClearColor(color.R, color.G, color.B, color.A); V_GL_ASSERT
	}

	vVoid vGlState::SetClearDepth(const vReal val) {
		if (mDepthClear == val) {
			return;
		}
		mDepthClear = val;
		glClearDepth(val); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetmDepthTest
	// --------------------------------------------------

	vVoid vGlState::SetDepthTest(const vInt depthTest) {
		if (mDepthTest == depthTest) {
			return;
		}
		mDepthTest = depthTest;
		if (mDepthTest != OFF) {
			glEnable(GL_DEPTH_TEST); V_GL_ASSERT
		} else {
			glDisable(GL_DEPTH_TEST); V_GL_ASSERT
		}
	}

	// --------------------------------------------------
	//	SetmmDepthFunc
	// --------------------------------------------------

	vVoid vGlState::SetDepthFunc(const vInt depthFunc) {
		if (mDepthFunc == depthFunc) {
			return;
		}
		mDepthFunc = depthFunc;
		glDepthFunc(mDepthFunc); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetDepthMask
	// --------------------------------------------------

	vVoid vGlState::SetDepthMask(const vInt depthMask) {
		if (mDepthMask == depthMask) {
			return;
		}
		mDepthMask = depthMask;
		glDepthMask(mDepthMask); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetStencilTest
	// --------------------------------------------------

	vVoid vGlState::SetStencilTest(const vInt stencilTest) {
		if (mStencilTest == stencilTest) {
			return;
		}
		mStencilTest = stencilTest;
		if (mStencilTest != OFF) {
			glEnable(GL_STENCIL_TEST); V_GL_ASSERT
		} else {
			glDisable(GL_STENCIL_TEST); V_GL_ASSERT
		}
	}

	// --------------------------------------------------
	//	SetStencilFunc
	// --------------------------------------------------

	vVoid vGlState::SetStencilFunc(const vInt stencilFunc, const vInt stencilRef, const vInt stencilMask) {
		if ((mStencilFunc == stencilFunc) && (mStencilRef == stencilRef) && (mStencilMask == stencilMask)) {
			return;
		}
		mStencilFunc = stencilFunc;
		mStencilRef	= stencilRef;
		mStencilMask = stencilMask;
		glStencilFunc(mStencilFunc, mStencilRef, mStencilMask); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetStencilOps
	// --------------------------------------------------

	vVoid vGlState::SetStencilOps(const vInt stencilFail, const vInt stencilZFail, const vInt stencilZPass)	{
		if ((mStencilFail == stencilFail) && (mStencilZFail == stencilZFail) && (mStencilZPass == stencilZPass)) {
			return;
		}
		mStencilFail = stencilFail;
		mStencilZFail = stencilZFail;
		mStencilZPass = stencilZPass;
		glStencilOp(mStencilFail, mStencilZFail, mStencilZPass); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetCulling
	// --------------------------------------------------

	vVoid vGlState::SetCulling(const vInt culling) {
		if (mCulling == culling) {
			return;
		}
		mCulling = culling;
		if (mCulling != OFF) {
			glEnable(GL_CULL_FACE); V_GL_ASSERT
		} else {
			glDisable(GL_CULL_FACE); V_GL_ASSERT
		}
	}

	// --------------------------------------------------
	//	SetCullingFunc
	// --------------------------------------------------

	vVoid vGlState::SetCullingFunc(const vInt cullingFunc) {
		if (mCullingFunc == cullingFunc) {
			return;
		}
		mCullingFunc = cullingFunc;
		glCullFace(mCullingFunc); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetCullingMode for front faces - CCW or CW
	// --------------------------------------------------
	vVoid vGlState::SetCullingMode(const vInt cullingMode) {
		if (mCullingMode == cullingMode) {
			return;
		}
		mCullingMode = cullingMode;
		glFrontFace(mCullingMode); V_GL_ASSERT
	}

	// --------------------------------------------------
	//	SetFill
	// --------------------------------------------------

	vVoid vGlState::SetFill(const vInt fill) {
		if (mFill == fill) {
			 return;
		}
		mFill = fill;
		glPolygonMode(GL_FRONT, mFill); V_GL_ASSERT
		glPolygonMode(GL_BACK, mFill); V_GL_ASSERT
	}

	vVoid vGlState::SetShading(const vInt shading) {
		if (mShading == shading) {
			return;
		}
		mShading = shading;
		glShadeModel(mShading); V_GL_ASSERT
	}

	vVoid vGlState::SetLighting(const vInt lighting) {
		if (mLighting == lighting) {
			return;
		}
		mLighting = lighting;
		glShadeModel(mShading); V_GL_ASSERT
	}

	vVoid vGlState::SetSpecular(const vInt specular) {
		if (mSpecular == specular) {
			return;
		}
		mSpecular = specular;
		glShadeModel(mShading); V_GL_ASSERT
	}

	vVoid vGlState::initTerms()	{
		// Blend
		OFF = GL_FALSE;
		ON = GL_TRUE;

		// Blend function
		ZERO = GL_ZERO;
		ONE = GL_ONE;
		SRC_COLOR = GL_SRC_COLOR;
		ONE_MINUS_SRC_COLOR = GL_ONE_MINUS_SRC_COLOR;
		DST_COLOR = GL_DST_COLOR;
		ONE_MINUS_DST_COLOR = GL_ONE_MINUS_DST_COLOR;
		SRC_ALPHA = GL_SRC_ALPHA;
		ONE_MINUS_SRC_ALPHA = GL_ONE_MINUS_SRC_ALPHA;
		DST_ALPHA = GL_DST_ALPHA;
		ONE_MINUS_DST_ALPHA = GL_ONE_MINUS_DST_ALPHA;

		// Alpha test
		//OFF = GL_FALSE;
		//ON = GL_TRUE;

		// Alpha function
		EQUAL = GL_EQUAL;
		LEQUAL = GL_LEQUAL;
		LESS = GL_LESS;
		NOTEQUAL = GL_NOTEQUAL;
		GREATER = GL_GREATER;
		GEQUAL = GL_GEQUAL;
		ALWAYS = GL_ALWAYS;
		NEVER = GL_NEVER;

		// Depth mask
		//OFF = GL_FALSE;
		//ON = GL_TRUE;

		// Depth function
		//EQUAL = GL_EQUAL;
		//LEQUAL = GL_LEQUAL;
		//LESS = GL_LESS;
		//NOTEQUAL = GL_NOTEQUAL;
		//GREATER = GL_GREATER;
		//GEQUAL = GL_GEQUAL;
		//ALWAYS = GL_ALWAYS;
		//NEVER = GL_NEVER;

		// Stencil
		//OFF = GL_FALSE;
		//ON = GL_TRUE;

		// Stencil function
		//EQUAL = GL_EQUAL;
		//LEQUAL = GL_LEQUAL;
		//LESS = GL_LESS;
		//NOTEQUAL = GL_NOTEQUAL;
		//GREATER = GL_GREATER;
		//GEQUAL = GL_GEQUAL;
		//ALWAYS = GL_ALWAYS;
		//NEVER = GL_NEVER;

		// Stencil operation
		KEEP = GL_KEEP;
		//ZERO = GL_ZERO;
		REPLACE = GL_REPLACE;
		INCR = GL_INCR;
		DECR = GL_DECR;
		INVERT = GL_INVERT;

		// mColor mask
		//ON = GL_FALSE;
		//OFF = GL_TRUE;

		// mCulling
		//OFF = GL_FALSE;
		//ON = GL_TRUE;

		// mCulling function
		FRONT = GL_FRONT;
		BACK = GL_BACK;
		CCW = GL_CCW;
		CW = GL_CW;

		// mShading
		FLAT = GL_FLAT;
		GOURAUD = GL_SMOOTH;

		// mFill
		SOLID = GL_FILL;
		WIRE = GL_LINE;
		POINTS = GL_POINTS;

		// mLighting
		//OFF = GL_FALSE;
		//ON = GL_TRUE;

		// mSpecular
		//OFF = GL_FALSE;
		//ON = GL_TRUE;
	}
}